/*
 * Copyright (C) 2011, EADS France
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 */

package xowl.interpreter.backends.jena;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *
 * @author Laurent WOUTERS
 */
public class JenaQueryEngine extends xowl.interpreter.impl.QueryEvaluator {
    protected JenaRepository repository;

    public JenaQueryEngine(JenaRepository repository) {
        this.repository = repository;
    }

    @Override public xowl.lang.runtime.Value eval_Contains(xowl.interpreter.impl.Evaluator parent, xowl.lang.actions.Contains query) throws xowl.interpreter.XOWLException {
        Job job = new Job(parent, query);
        return job.contains();
    }
    @Override public List<xowl.interpreter.QuerySolution> select(xowl.interpreter.impl.Evaluator parent, xowl.lang.actions.Select query) throws xowl.interpreter.XOWLException {
        Job job = new Job(parent, query);
        return job.select();
    }

    
    protected class Job {
        protected xowl.interpreter.impl.Evaluator evaluator;
        protected xowl.lang.actions.Query original;
        protected xowl.interpreter.backends.rdf.Translation xOWLTriples;
        protected JenaGraphTranslator.Translation jenaTriples;
        protected List<xowl.interpreter.QuerySolution> results;

        public Job(xowl.interpreter.impl.Evaluator evaluator, xowl.lang.actions.Query query) {
            this.evaluator = evaluator;
            this.original = query;
            this.results = new ArrayList<xowl.interpreter.QuerySolution>();
        }

        public xowl.lang.runtime.Value contains() throws xowl.interpreter.XOWLException {
            if (original.getAllAxioms().size() == 1 && original.getAllAxioms().iterator().next() instanceof xowl.lang.actions.FunctionDefinition)
                return contains_FunctionDefinition(evaluator, repository, (xowl.lang.actions.FunctionDefinition)original.getAllAxioms().iterator().next());

            // Translate XOWL axioms to XOWL RDF triples
            xOWLTriples = repository.getXOWLTranslator().translateAxioms(evaluator, false, true, original.getAllAxioms());
            // Translate XOWL RDF triples to Jena graph triples
            jenaTriples = repository.getGraphTranslator().translate(xOWLTriples.getTriples());
            // execute
            return contains_do();
        }

        private xowl.lang.runtime.Value contains_do() throws xowl.interpreter.XOWLException {
            // Build query
            com.hp.hpl.jena.query.Query query = com.hp.hpl.jena.query.QueryFactory.make();
            query.setQuerySelectType();
            com.hp.hpl.jena.sparql.syntax.ElementGroup group = new com.hp.hpl.jena.sparql.syntax.ElementGroup();
            for (com.hp.hpl.jena.graph.Triple triple : jenaTriples.getTriples())
                group.addTriplePattern(triple);
            query.setQueryPattern(group);

            // Execute query
            com.hp.hpl.jena.query.QueryExecution exec = com.hp.hpl.jena.query.QueryExecutionFactory.create(query, repository.getJenaModel());
            try {
                com.hp.hpl.jena.query.ResultSet rs = exec.execSelect() ;
                return xowl.utils.Values.getValue(repository, rs.hasNext());
            }
            catch (java.lang.Exception ex) {
                exec.close();
                repository.getLog().fatal(JenaRepository.name, "Querying Jena fact base caused an exception", ex);
                throw new xowl.interpreter.XOWLException("Repository is inconsistent");
            }
            finally { exec.close(); }
        }

        public List<xowl.interpreter.QuerySolution> select() throws xowl.interpreter.XOWLException {
            if (original.getAllAxioms().size() == 1 && original.getAllAxioms().iterator().next() instanceof xowl.lang.actions.FunctionDefinition) {
                return select_FunctionDefinition(evaluator, repository, (xowl.lang.actions.FunctionDefinition)original.getAllAxioms().iterator().next());
            } else {
                // Translate XOWL axioms to XOWL RDF triples
                xOWLTriples = repository.getXOWLTranslator().translateAxioms(evaluator, false, true, original.getAllAxioms());
                // Translate XOWL RDF triples to Jena graph triples
                jenaTriples = repository.getGraphTranslator().translate(xOWLTriples.getTriples());
                // execute
                select_do();
            }
            return results;
        }

        private void select_do() throws xowl.interpreter.XOWLException {
            // Build query
            com.hp.hpl.jena.query.Query query = com.hp.hpl.jena.query.QueryFactory.make();
            query.setQuerySelectType();
            com.hp.hpl.jena.sparql.syntax.ElementGroup group = new com.hp.hpl.jena.sparql.syntax.ElementGroup();
            for (com.hp.hpl.jena.graph.Triple triple : jenaTriples.getTriples())
                group.addTriplePattern(triple);
            query.setQueryPattern(group);
            for (com.hp.hpl.jena.sparql.core.Var var : jenaTriples.getVariables())
                query.addResultVar(var);
            
            // Execute query
            com.hp.hpl.jena.query.QueryExecution exec = com.hp.hpl.jena.query.QueryExecutionFactory.create(query, repository.getJenaModel());
            try {
                com.hp.hpl.jena.query.ResultSet rs = exec.execSelect();
                while (rs.hasNext()) {
                    com.hp.hpl.jena.query.QuerySolution jenaSolution = rs.nextSolution();
                    Map<xowl.lang.actions.QueryVariable, xowl.lang.runtime.Value> map = new HashMap<xowl.lang.actions.QueryVariable, xowl.lang.runtime.Value>();
                    for (com.hp.hpl.jena.sparql.core.Var var : jenaTriples.getVariables()) {
                        xowl.interpreter.backends.rdf.RDFNode node = repository.getRDFNode(jenaSolution.get(var.getVarName()));
                        if (node != null)
                            map.put(jenaTriples.getVariableNode(var).getVariable(), repository.getValue(node));
                    }
                    results.add(new xowl.interpreter.QuerySolution(map));
                }
            }
            catch (java.lang.Exception ex) {
                exec.close();
                repository.getLog().fatal(JenaRepository.name, "Querying Jena fact base caused an exception", ex);
                throw new xowl.interpreter.XOWLException("Repository is inconsistent");
            }
            finally { exec.close(); }
        }
    }
}
